#ifndef     __UART_H
#define     __UART_H

#include "./dma.h"
#include "./gpio.h"

namespace device::uart {

using namespace dma;
using namespace gpio;

#define UART(BASE)      reinterpret_cast<USART_TypeDef *>(BASE)

template <uint32_t BASE, uint32_t BRR, 
    typename TX_PIN, typename RX_PIN, 
    typename TX_DMA, typename TX_DMA_CHAN,
    typename RX_DMA, typename RX_DMA_CHAN> 
struct Uart {
    
    static inline void init(void* recv_buf, uint32_t len) {
        config_af_pp_out<TX_PIN>();
        config_float_input<RX_PIN>();

        UART(BASE)->CR1 = 0x00;
        UART(BASE)->CR2 = 0x00;
        UART(BASE)->CR3 = USART_CR3_DMAR | USART_CR3_DMAT;
        UART(BASE)->BRR = BRR;
        UART(BASE)->CR1 = 0x201C;
        RX_DMA::clear_flags();
        RX_DMA_CHAN::init_with_recv_uart(
            reinterpret_cast<uint32_t>(recv_buf), 
            reinterpret_cast<uint32_t>(&UART(BASE)->DR), 
            len
        );
    }

    static inline void write(const void *buf, uint8_t len) {
        TX_DMA::clear_flags();
        TX_DMA_CHAN::init_with_write_uart(
            reinterpret_cast<uint32_t>(buf),
            reinterpret_cast<uint32_t>(&UART(BASE)->DR),
            len
        );
    }

    static inline bool is_idle() {
        return (UART(BASE)->SR & USART_SR_IDLE) != 0;
    }

    static inline void clear_idle_flag() {
        uint32_t clr = UART(BASE)->DR;
    }
};

}


#endif
